package game_grpc

import (
	"context"
	"fmt"
	"game-server/config"
	"log"
	"pb/proto"
	"sync"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/connectivity"
	"google.golang.org/grpc/credentials/insecure"
)

var (
	// matchClient 全局匹配服务的gRPC客户端
	matchClient pb.MatcherGrpcServiceClient
	// matchClientMu 保护 matchClient 的并发访问
	matchClientMu sync.RWMutex
	// 最大重试次数
	maxRetries = 5
)

// GetMatchClient 安全地获取 matchClient
func GetMatchClient() pb.MatcherGrpcServiceClient {
	matchClientMu.RLock()
	defer matchClientMu.RUnlock()
	return matchClient
}

// InitMatchGrpc 初始化匹配grpc
func InitMatchGrpc(config config.Config, serverCtx context.Context) error {
	// 构建目标地址
	target := fmt.Sprintf("%s:%d", config.MathGrpc.Host, config.MathGrpc.Port)
	conn, err := createGRPCConn(target)
	if err != nil {
		return fmt.Errorf("无法建立gRPC连接: %v", err)
	}

	// 创建客户端
	matchClientMu.Lock()
	matchClient = pb.NewMatcherGrpcServiceClient(conn)
	matchClientMu.Unlock()

	// 监听服务器关闭信号
	go func() {
		<-serverCtx.Done()
		log.Println("正在关闭匹配服务gRPC连接...")
		if err := conn.Close(); err != nil {
			log.Printf("关闭匹配服务gRPC连接时发生错误: %v", err)
			return
		}
		log.Println("匹配服务gRPC连接已关闭")

		// 清理客户端
		matchClientMu.Lock()
		matchClient = nil
		matchClientMu.Unlock()
	}()

	log.Printf("已成功连接到匹配服务器: %s", target)
	return nil
}

// 尝试链接grpc服务
func createGRPCConn(target string) (*grpc.ClientConn, error) {
	var conn *grpc.ClientConn
	var err error
	backoff := 1 * time.Second

	log.Printf("开始尝试连接到 %s，最大重试次数: %d", target, maxRetries)

	for i := 0; i < maxRetries; i++ {
		log.Printf("尝试连接 #%d/%d...", i+1, maxRetries)

		// 为每次重试创建新的上下文，避免受到原始上下文超时的影响
		retryCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)

		// 建立连接，使用 WithBlock() 强制等待连接建立
		conn, err = grpc.DialContext(
			retryCtx,
			target,
			grpc.WithTransportCredentials(insecure.NewCredentials()),
			//当使用 WithBlock() 时，如果上下文超时，连接尝试会立即失败
			grpc.WithBlock(), // 强制等待连接建立, grpc是懒加载的
		)

		// 连接尝试完成后取消上下文
		cancel()

		if err != nil {
			log.Printf("连接失败 %d: %v", i+1, err)
			time.Sleep(backoff)
			backoff *= 2
			continue
		}

		// 验证连接状态
		state := conn.GetState()
		log.Printf("连接状态 %d: %s", i+1, state.String())

		// 如果连接不是就绪状态，尝试等待它变为就绪
		if state != connectivity.Ready {
			// 创建一个新的上下文，用于等待状态变化
			waitCtx, waitCancel := context.WithTimeout(context.Background(), 3*time.Second)

			// 等待连接状态变化或超时
			log.Printf("等待连接就绪...")
			changed := conn.WaitForStateChange(waitCtx, state)
			waitCancel()

			if !changed {
				// 如果状态没有变化，关闭连接并重试
				log.Printf("等待连接就绪超时")
				conn.Close()
				time.Sleep(backoff)
				backoff *= 2
				continue
			}

			// 获取新状态
			newState := conn.GetState()
			log.Printf("连接状态变为: %s", newState.String())

			// 如果新状态仍然不是就绪，关闭连接并重试
			if newState != connectivity.Ready {
				log.Printf("连接状态不是就绪状态")
				conn.Close()
				time.Sleep(backoff)
				backoff *= 2
				continue
			}
		}

		log.Printf("连接成功建立!")
		return conn, nil
	}

	if conn != nil {
		conn.Close()
	}
	return nil, fmt.Errorf("达到最大重试次数 %d，无法建立连接: %v", maxRetries, err)
}
